Architecture
This document provides an overview of the FF-API-External architecture, including its core components, design patterns, and how the various parts of the system interact.
Table of Contents
- System Overview
- Core Components
- Design Patterns
- Interaction Flow
- Dependency Management
- Error Handling
- Extensibility
System Overview
FF-API-External is a Flask-based API service that integrates with numerous third-party services. Its architecture follows a modular design with clear separation of concerns between routing, business logic, and data access.
The high-level architecture can be visualized as follows:
Client Request
↓
[Nginx/Apache] → Reverse Proxy/Load Balancer
↓
[WSGI Server] → Gunicorn/uWSGI
↓
[Flask Application]
↓
┌─────────────────┬─────────────────┬─────────────────┐
│ API Routes │ Service Models │ Database Utils │
└─────────────────┴─────────────────┴─────────────────┘
↓ ↓ ↓
┌─────────────────┬─────────────────┬─────────────────┐
│ Third-Party │ MySQL │ MongoDB │
│ Services │ Database │ Database │
└─────────────────┴─────────────────┴─────────────────┘
Core Components
Flask Application
The Flask application is the central component that initializes the web server and registers all the API routes. It's defined in app.py and configured in server.py:
# app.py
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
# server.py
import inspect
import os
import sys
import datetime
from app import app
# Util imports
from utils.json_response import JsonResponse
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0, parentdir)
# Response object
resp = JsonResponse()
# API route imports
from view.api.ai.openai.chatgpt import chatgpt_route
from view.api.ai.anthropic.claude import claude_route
# ... other route imports
# Inject Registered Blueprints
app.register_blueprint(chatgpt_route)
app.register_blueprint(claude_route)
# ... other blueprint registrations
API Routes
API routes are organized as Flask Blueprints, with each blueprint corresponding to a specific third-party service or functional area. They're defined in the view/api/ directory:
# Example from view/api/ai/anthropic/claude.py
from flask import Blueprint, request
from flask_cors import cross_origin
from models.ai.anthropic.claude import ClaudeAI
from utils.json_response import JsonResponse
claude_route = Blueprint('claude_route', __name__)
resp = JsonResponse()
@claude_route.route('/api/v1/claude/prompt', methods=['POST'])
@cross_origin()
def api_v1_claude_prompt():
try:
claude = ClaudeAI()
prompt = request.form.get("prompt")
max_tokens = int(request.form.get("max_tokens", 1000))
temperature = float(request.form.get("temperature", 0.7))
system_prompt = request.form.get("system_prompt")
response = claude.generate_response(
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature,
system_prompt=system_prompt
)
return resp.returnResponse(200, response)
except Exception as e:
return resp.returnResponse(400, f"Error: {str(e)}")
Service Models
Service models encapsulate the business logic for interacting with third-party services. They're defined in the models/ directory:
# Example from models/ai/anthropic/claude.py
import anthropic
class ClaudeAI:
def __init__(self):
self.api_key = config('CLAUDE_API_KEY')
self.client = anthropic.Anthropic(api_key=self.api_key)
self.available_models = {
"claude-3-opus-20240229",
"claude-3-sonnet-20240229",
"claude-3-haiku-20240307",
"claude-3-5-sonnet-20240620",
"claude-3-7-sonnet-20250219"
}
def generate_response(self, prompt, max_tokens=1000, temperature=0.7, system_prompt=None):
# Implementation to generate response from Claude AI
# ...
Database Utilities
Database utilities provide standardized methods for interacting with the databases. They're defined in the dbutils/ directory:
# Example from dbutils/mongodb.py
class MongoDB:
def __init__(self, no_http=False):
self.no_http = no_http
try:
self.mclient = MongoClient(config('MONGODB_MAIN_PROD'))
except FileNotFoundError:
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
self.mclient = MongoClient(config('MONGODB_MAIN_DEV_PREFIX') + parentdir + "/mongodb-1-cert.crt")
self.resp = JsonResponse()
def insert_or_update(self, db, col, filter, update):
"""
Inserts or updates a document in a MongoDB collection based on the filter.
"""
# Implementation...
# Example from dbutils/mysql.py
class MySQL:
def __init__(self):
self.conn = mysql.connection
self.cur = self.conn.cursor()
self.resp = JsonResponse()
def select(self, select):
"""Execute a SELECT query and return the results."""
# Implementation...
Utility Functions
Utility functions provide reusable functionality for common tasks such as JSON response formatting, validation, and HTTP requests. They're defined in the utils/ directory:
# Example from utils/json_response.py
class JsonResponse:
def returnResponse(
self,
code: int = 200,
msg: Any = "",
exec_time: int = 0,
cached: bool = False,
misc: Optional[Any] = None
):
"""Format a standardized JSON response."""
# Implementation...
# Example from utils/validation.py
class Validation:
def is_valid_email(self, email):
"""Validates the given email address using a regular expression."""
regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
return bool(re.fullmatch(regex, email))
Design Patterns
FF-API-External employs several design patterns to ensure maintainability, scalability, and separation of concerns:
Factory Pattern
The service models act as factories that create and manage instances of third-party clients:
class GSMArena:
def __init__(self):
self.mdb = MongoDB()
self.gsm_arena_base_url = "https://www.gsmarena.com/"
def get_brand_list(self):
# Implementation...
Decorator Pattern
Flask decorators are used to add functionality to routes:
@serp_google_place_route.route('/api/v1/place/serp/search', methods=['GET'])
@expected_params(['q', 'll'])
@cross_origin()
def api_v1_place_serp_search():
# Implementation...
Custom decorators are used for parameter validation:
def required_params(req_keys):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Validation logic...
return func(*args, **kwargs)
return wrapper
return decorator
Repository Pattern
The database utilities implement the repository pattern for data access:
class MongoDB:
def find_many(self, db, col, query=None, sortkey=None, sortorder=None, limit=None, skip=0, projection=None):
# Implementation...
Adapter Pattern
Service models act as adapters for third-party APIs, providing a consistent interface:
class VincarioVINDecoder:
def __init__(self, api_key, secret_key):
self.api_key = api_key
self.secret_key = secret_key
self.base_url = "https://api.vindecoder.eu/3.0"
def decode(self, vin):
# Implementation...
Interaction Flow
The typical flow of a request through the system is as follows:
- Client Request: A client sends an HTTP request to an API endpoint
- Route Handling: The appropriate Flask route handler processes the request
- Parameter Validation: Request parameters are validated
- Service Model: The route handler instantiates a service model
- Business Logic: The service model executes the business logic
- External API or Database: The service model interacts with external APIs or databases
- Response Formatting: The response is formatted using the
JsonResponseutility - Client Response: The formatted response is returned to the client
Example of this flow:
Client → [GET /api/v1/gsmarena/brands]
↓
Flask Route (gsmarena_route.py)
↓
GSMArena Model (gsmarena.py)
↓
MongoDB Utility (mongodb.py)
↓
MongoDB Database
↓
JsonResponse Utility (json_response.py)
↓
Client ← [JSON Response]
Dependency Management
Dependencies are managed through the requirements.txt file, which specifies the required Python packages and their versions:
ago==0.0.95
aniso8601==9.0.1
annotated-types==0.6.0
anthropic~=0.43.0
anyio==3.6.2
# ... other dependencies
Key dependencies include:
- Flask: Web framework
- Flask-CORS: Cross-Origin Resource Sharing
- Flask-MySQLdb: MySQL integration
- PyMongo: MongoDB integration
- Requests: HTTP client
- Anthropic: Claude AI client
- OpenAI: ChatGPT client
Error Handling
Error handling is implemented at multiple levels:
Route Level
Try-except blocks in route handlers catch exceptions and return formatted error responses:
@claude_route.route('/api/v1/claude/prompt', methods=['POST'])
@cross_origin()
def api_v1_claude_prompt():
try:
# Implementation...
return resp.returnResponse(200, response)
except Exception as e:
return resp.returnResponse(400, f"Error: {str(e)}")
Service Model Level
Service models include error handling for third-party API calls:
def get_vehicle_makes_by_year(self, year):
try:
url = f"{self.base_url}/car-lists/get/makes/{year}"
response = requests.get(url, headers=self.headers)
response.raise_for_status() # Raise exception for HTTP errors
return response.json()
except requests.exceptions.RequestException as e:
logging.error(f"Error in get_vehicle_makes_by_year: {str(e)}")
return {"error": str(e)}
Database Level
Database utilities include error handling for database operations:
def insert_or_update(self, db, col, filter, update):
try:
# Implementation...
except (PyMongoError, TypeError) as e:
logging.error("MongoDB Error: %s", str(e))
return self.resp.returnResponse(400, f"MDB Error: {str(e)}")
Extensibility
The modular architecture of FF-API-External makes it easily extensible:
Adding a New Service
To add a new third-party service:
- Create a new service model in the
models/directory - Create a new route blueprint in the
view/api/directory - Register the blueprint in
server.py
Adding a New Endpoint
To add a new endpoint for an existing service:
- Add a new route handler to the appropriate blueprint
- Implement the required functionality in the service model
Adding a New Database
To add support for a new database:
- Create a new database utility in the
dbutils/directory - Implement the standard methods (insert, update, select, etc.)
- Update the relevant service models to use the new database utility